package com.haizhi.storage;

import com.haizhi.util.GenerateMd5;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermission;
import java.util.*;
import java.util.stream.Collectors;

@Service("storageService")
public class StorageServiceImpl implements StorageService {

    private final static String UP_LOCATION = "client-upload-dir";
    private final static String DOWN_LOCATION = "client-download-dir";
    private final static String TMP_LOCATION = "tmp";


    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private final Path upLocation;
    private final Path downLocation;
    private final Path tmpLocation;

    private final DownloadRepository downloadRepository;
    private final UploadRepository uploadRepository;

    // 存储md5 信息
    private Map<String, FilesInfo> storageInfoMap;

    @Autowired
    public StorageServiceImpl(Environment ev, DownloadRepository downloadRepository, UploadRepository uploadRepository) {
        this.upLocation = Paths.get(ev.getProperty("upload.location", UP_LOCATION));
        this.downLocation = Paths.get(ev.getProperty("download.location", DOWN_LOCATION));
        this.tmpLocation = Paths.get(ev.getProperty("tmp.location", TMP_LOCATION));

        this.downloadRepository = downloadRepository;
        this.uploadRepository = uploadRepository;

        storageInfoMap = new HashMap<>();
    }

    @Override
    public String getUpLocation() {
        return this.upLocation.toString();
    }

    @Override
    public String getDownLocation() {
        return this.downLocation.toString();
    }

    @Override
    public String getTmpLocation() {
        return this.tmpLocation.toString();
    }

    @Override
    public void storageInit() {
        try {
            //文件夹权限设置
            Set<PosixFilePermission> perms = new HashSet<>();
            perms.add(PosixFilePermission.OWNER_READ);
            perms.add(PosixFilePermission.OWNER_WRITE);
            perms.add(PosixFilePermission.OWNER_EXECUTE);
            perms.add(PosixFilePermission.GROUP_READ);
            perms.add(PosixFilePermission.GROUP_WRITE);
            perms.add(PosixFilePermission.GROUP_EXECUTE);
            perms.add(PosixFilePermission.OTHERS_WRITE);
            perms.add(PosixFilePermission.OTHERS_READ);
            perms.add(PosixFilePermission.OTHERS_EXECUTE);

            if (Files.notExists(upLocation)) {
                Files.createDirectories(upLocation);
            }
            if (Files.exists(upLocation)) {
                Files.setPosixFilePermissions(upLocation, perms);
            } else {
                throw new IOException(upLocation + "文件夹不存在...");
            }

            if (Files.notExists(downLocation)) {
                Files.createDirectories(downLocation);
            }
            if (Files.exists(downLocation)) {
                Files.setPosixFilePermissions(downLocation, perms);
            } else {
                throw new IOException(downLocation + "文件夹不存在...");
            }

            if (Files.notExists(tmpLocation)) {
                Files.createDirectories(tmpLocation);
            }

            logger.info("临时文件路径: tmpLocation = {}", tmpLocation.toAbsolutePath());
            logger.info("上传路径: upLocation = {}", upLocation.toAbsolutePath());
            logger.info("下载路径: downLocation = {}", downLocation.toAbsolutePath());
            logger.info("目录初始化完成...");
        } catch (IOException e) {
            logger.error("ERROR:", e);
            throw new StorageException("Could not initialize storage", e);
        }
    }

    @Override
    public List<String> loadAllUpload() {
        try {
            return Files.walk(this.upLocation)
                    .filter(path -> !path.equals(this.upLocation) && !Files.isDirectory(path))
                    .map(this.upLocation::relativize).map(Path::toString).collect(Collectors.toList());
        } catch (IOException e) {
            logger.error("ERROR:", e);
            throw new StorageException("Failed to read stored files", e);
        }
    }

    @Override
    public String getFileMD5(String filename) {

        String md5 = "";
        try {

            Path filepath = Paths.get(filename);
            logger.info("当前需要获取MD5的文件名称: filename = {}", filename);
            logger.info("当前需要获取MD5的文件路径: filepath = {}", filepath);
            if (!Files.exists(filepath)) {
                logger.error("文件不存在: file = {}", filename);
                return md5;
            }

            String fileTime = Files.getLastModifiedTime(filepath).toString();
            logger.info("file = {} lastTime = {}", filename, fileTime);
            if (!storageInfoMap.containsKey(filename)) {
                md5 = GenerateMd5.getHash(filepath);
                storageInfoMap.put(filename, new FilesInfo(md5, filename, fileTime));
                return md5;
            }

            // 如果最后修改时间一致
            FilesInfo filesInfo = storageInfoMap.get(filename);
            if (Objects.equals(filesInfo.getLastTime(), fileTime)) {
                return filesInfo.getMd5();
            }

            md5 = GenerateMd5.getHash(filepath);
            filesInfo.setMd5(md5);
            filesInfo.setLastTime(fileTime);
        } catch (Exception e) {
            logger.error("ERROR", e);
        }

        return md5;
    }

    @Override
    public DownloadInfo findDownloadOne(String filename) {
        if (filename == null || Objects.equals(filename, "")) {
            return null;
        }
        DownloadInfo downloadInfo;
        try {
            downloadInfo = downloadRepository.findByFileName(filename);
        } catch (Exception e) {
            downloadInfo = null;
            logger.error("ERROR:", e);
        }

        return downloadInfo;
    }

    @Override
    public boolean saveDownload(String fileName, String fileBase64, String md5) {
        DownloadInfo storageInfo = new DownloadInfo(fileName, fileBase64, md5);
        return this.saveDownload(storageInfo);
    }

    @Override
    public boolean saveDownload(DownloadInfo downloadInfo) {
        try {
            downloadRepository.save(downloadInfo);
        } catch (Exception e) {
            logger.error("ERROR:", e);
            return false;
        }
        return true;
    }

    @Override
    public boolean deleteAllDown() {
        try {
            downloadRepository.deleteAll();
        } catch (Exception e) {
            logger.error("ERROR:", e);
            return false;
        }
        return true;
    }

    @Override
    public boolean saveAllDown(List<DownloadInfo> downloadInfoList) {
        try {
            downloadRepository.save(downloadInfoList);
        } catch (Exception e) {
            logger.error("ERROR:", e);
            return false;
        }
        return true;
    }

    @Override
    public UploadInfo findUploadOne(String filename) {
        if (filename == null || Objects.equals(filename, "")) {
            return null;
        }

        UploadInfo uploadInfo;
        try {
            uploadInfo = uploadRepository.findByFileName(filename);
        } catch (Exception e) {
            uploadInfo = null;
            logger.error("ERROR:", e);
        }

        return uploadInfo;
    }

    @Override
    public boolean saveUpload(String fileName, String fileBase64, String md5) {
        UploadInfo uploadInfo = new UploadInfo(fileName, fileBase64, md5);
        return this.saveUpload(uploadInfo);
    }

    @Override
    public boolean saveUpload(UploadInfo uploadInfo) {
        try {
            uploadRepository.save(uploadInfo);
        } catch (Exception e) {
            logger.error("ERROR:", e);
            return false;
        }
        return true;
    }

    @Override
    public boolean deleteAllUp() {
        try {
            uploadRepository.deleteAll();
        } catch (Exception e) {
            logger.error("ERROR:", e);
            return false;
        }
        return true;
    }

    @Override
    public boolean saveAllUp(List<UploadInfo> uploadInfoList) {
        try {
            uploadRepository.save(uploadInfoList);
        } catch (Exception e) {
            logger.error("ERROR:", e);
            return false;
        }
        return true;
    }
}
